import os
import subprocess
import re
import json
import argparse
from pydub import AudioSegment
from pydub.silence import detect_silence

def detect_silent_intervals(input_file, silence_thresh=-50, min_silence_len=1000, seek_step=10):
    """
    检测视频中的静音区间
    
    参数:
    input_file: 输入视频文件路径
    silence_thresh: 静音阈值 (dBFS)
    min_silence_len: 最小静音持续时间 (毫秒)
    seek_step: 检测步长 (毫秒)
    
    返回:
    静音区间列表 [(start1, end1), (start2, end2), ...]
    """
    # 临时音频文件路径
    temp_audio = "temp_audio.wav"
    
    try:
        # 使用FFmpeg提取音频
        cmd = [
            'ffmpeg',
            '-i', input_file,
            '-vn',          # 禁用视频
            '-ac', '1',     # 单声道
            '-ar', '16000', # 采样率16kHz
            '-y',           # 覆盖输出文件
            temp_audio
        ]
        
        subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        # 加载音频文件
        audio = AudioSegment.from_file(temp_audio)
        
        # 检测静音区间
        silence_intervals = detect_silence(
            audio,
            min_silence_len=min_silence_len,
            silence_thresh=silence_thresh,
            seek_step=seek_step
        )
        
        # 转换为秒
        silence_intervals = [(start/1000, end/1000) for start, end in silence_intervals]
        
        return silence_intervals
        
    except Exception as e:
        print(f"检测静音时出错: {str(e)}")
        return []
    finally:
        # 清理临时文件
        if os.path.exists(temp_audio):
            os.remove(temp_audio)

def detect_silent_intervals_ffmpeg(input_file, silence_thresh=-50, min_silence_duration=1.0):
    """
    使用FFmpeg直接检测静音区间（不需要临时文件）
    
    参数:
    input_file: 输入视频文件路径
    silence_thresh: 静音阈值 (dB)
    min_silence_duration: 最小静音持续时间 (秒)
    
    返回:
    静音区间列表 [(start1, end1), (start2, end2), ...]
    """
    try:
        # 构建FFmpeg命令
        cmd = [
            'ffmpeg',
            '-i', input_file,
            '-af',
            f"silencedetect=n={silence_thresh}dB:d={min_silence_duration}",
            '-f', 'null',
            '-'
        ]
        
        # 运行命令并捕获输出
        result = subprocess.run(
            cmd,
            stderr=subprocess.PIPE,
            universal_newlines=False,  # 确保返回字节
            check=True
        )
        
        # 尝试使用UTF-8解码，如果失败则使用错误忽略策略
        try:
            output = result.stderr.decode('utf-8')
        except UnicodeDecodeError:
            output = result.stderr.decode('utf-8', errors='ignore')
        
        # 解析输出
        silence_intervals = []
        
        # 正则表达式匹配静音区间
        start_pattern = r"silence_start: (\d+\.?\d*)"
        end_pattern = r"silence_end: (\d+\.?\d*)"
        
        starts = [float(match) for match in re.findall(start_pattern, output)]
        ends = [float(match) for match in re.findall(end_pattern, output)]
        
        # 确保开始和结束时间配对
        if len(starts) == len(ends):
            silence_intervals = list(zip(starts, ends))
        elif len(starts) > len(ends):
            # 处理最后一个静音区间可能没有结束时间的情况
            for i in range(len(ends)):
                silence_intervals.append((starts[i], ends[i]))
            # 获取视频时长作为最后一个区间的结束时间
            duration = get_video_duration(input_file)
            if duration > starts[-1]:
                silence_intervals.append((starts[-1], duration))
        
        return silence_intervals
        
    except subprocess.CalledProcessError as e:
        print(f"FFmpeg错误: {e.stderr.decode('utf-8', errors='ignore')}")
        return []
    except Exception as e:
        print(f"检测静音时出错: {str(e)}")
        return []



def get_video_duration(input_file):
    """获取视频总时长（秒）"""
    try:
        cmd = [
            'ffprobe',
            '-v', 'error',
            '-show_entries', 'format=duration',
            '-of', 'json',
            input_file
        ]
        
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        data = json.loads(result.stdout)
        return float(data['format']['duration'])
    except Exception:
        return 0

def format_time(seconds):
    """将秒数格式化为 HH:MM:SS.ms """
    hours = int(seconds // 3600)
    minutes = int((seconds % 3600) // 60)
    seconds = seconds % 60
    return f"{hours:02d}:{minutes:02d}:{seconds:06.3f}"

def main():
    parser = argparse.ArgumentParser(description='检测视频中的静音区间')
    parser.add_argument('input', help='输入视频文件路径')
    parser.add_argument('-t', '--threshold', type=float, default=-30,
                        help='静音阈值 (dB, 默认-30)')
    parser.add_argument('-d', '--min-duration', type=float, default=1.0,
                        help='最小静音持续时间 (秒, 默认1.0)')
    parser.add_argument('-m', '--method', choices=['ffmpeg', 'pydub'], default='ffmpeg',
                        help='检测方法 (ffmpeg或pydub, 默认ffmpeg)')
    parser.add_argument('-o', '--output', default="detect_silence_output.txt", help='输出结果文件 (可选)')
    
    args = parser.parse_args()
    
    if not os.path.isfile(args.input):
        print(f"错误: 输入文件不存在 - {args.input}")
        return
    
    # 删除上一次的输出文件
    if args.output and os.path.isfile(args.output):
        os.remove(args.output)
    
    print(f"分析视频: {args.input}")
    print(f"静音阈值: {args.threshold} dB")
    print(f"最小静音持续时间: {args.min_duration} 秒")
    
    if args.method == 'pydub':
        print("使用pydub方法检测...")
        # 将秒转换为毫秒
        min_silence_ms = int(args.min_duration * 1000)
        intervals = detect_silent_intervals(
            args.input,
            silence_thresh=args.threshold,
            min_silence_len=min_silence_ms
        )
    else:
        print("使用FFmpeg方法检测...")
        intervals = detect_silent_intervals_ffmpeg(
            args.input,
            silence_thresh=args.threshold,
            min_silence_duration=args.min_duration
        )
    
    # 获取视频总时长
    total_duration = get_video_duration(args.input)
    
    if intervals:
        print("\n检测到的静音区间:")
        print("=" * 60)
        print(f"{'开始时间':<15} {'结束时间':<15} {'持续时间':<15} {'位置(%)':<10}")
        print("-" * 60)
        
        output_lines = []
        
        for i, (start, end) in enumerate(intervals):
            duration = end - start
            position = (start / total_duration * 100) if total_duration > 0 else 0
            
            # 格式化输出
            start_str = format_time(start)
            end_str = format_time(end)
            duration_str = f"{duration:.3f}秒"
            position_str = f"{position:.1f}%"
            
            print(f"{start_str:<15} {end_str:<15} {duration_str:<15} {position_str:<10}")
            output_lines.append(f"{start_str}\t{end_str}\t{duration_str}\t{position_str}")
        
        print("=" * 60)
        total_silence = sum(end - start for start, end in intervals)
        silence_percent = (total_silence / total_duration * 100) if total_duration > 0 else 0
        print(f"总静音时间: {total_silence:.3f}秒 ({silence_percent:.2f}%)")
        
        # 输出到文件
        if args.output:
            # 保存结果到文件 utf-8 编码
            with open(args.output, 'w', encoding='utf-8') as f:
                f.write("开始时间\t结束时间\t持续时间\t位置(%)\n")
                f.write("\n".join(output_lines))
                f.write(f"\n\n总静音时间: {total_silence:.3f}秒 ({silence_percent:.2f}%)")
            print(f"\n结果已保存到: {args.output}")
    else:
        print("\n未检测到静音区间")

if __name__ == "__main__":
    main()